/*
 * Syncrem - Tool for remote synchronizing
 *
 * Copyright (c) 2011 Norbert Chromek <norbert.chromek@gmail.com>
 *
 * Syncrem is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Syncrem is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Syncrem. If not, see <http://www.gnu.org/licenses/>.
 */

#include "errormessage.h"
#include "infomessage.h"
#include "process.h"

#include <iostream>

#include <sys/stat.h>

/**
 * Constructor.
 */

Process::Process() :
	level_(Process::User),
	logHandler_(LogHandler()),
	lock_(FileLock()),
	signalHandler_(SignalHandler()) {

}

/**
 * Destructor.
 */

Process::~Process() {

}

/**
 * This function checks whether lock file is locked or not.
 */

Process::LockStatus Process::checkLock() {
	if(lock_.status() != FileLock::Unlocked) {
		std::cout << "Syncrem daemon is already running." << std::endl;
		return Process::Locked;
	}
	return Process::Unlocked;
}

/**
 * This function checks whether process is running
 * with super user privileges.
 */

Process::UserStatus Process::checkUser() {
	if(getuid() != 0 || geteuid() != 0) {
		std::cout << "Syncrem daemon has to be run as root." << std::endl;
		return Process::UserNonRoot;
	}
	return Process::UserRoot;
}

/**
 * This function turns process to daemon running
 * on background.
 */

Process::ForkStatus Process::daemonize() {

	// checks whether process is already a daemon and
	// further processing of function is not needed
	if(getpid() == 1) return Process::ForkedProcess;

	switch(forkProcess()) {
		case -1:
			std::cout << "Process forking failed. Terminating..." << std::endl;
			return Process::ForkingFailed;
		case 0:
			// locks lock file for daemonized process
			if(!lock_.lock()) {
				logHandler_ << ErrorMessage("Can not lock access to a lock file. Terminating...");
				return Process::ForkingFailed;
			}

			// sets file creating mask to zero
			umask(0);

			// creates new session for forked process
			if (setsid() < 0) {
				logHandler_ << ErrorMessage("Session creating failed. Terminating...");
				return Process::ForkingFailed;
			}

			// sets working directory to root
			if ((chdir("/")) < 0) {
				logHandler_ << ErrorMessage("Can not change working directory to root. Terminating...");
				return Process::ForkingFailed;
			}

			// closes out the standard file descriptors
			close(STDIN_FILENO);
			close(STDOUT_FILENO);
			close(STDERR_FILENO);

			logHandler_ << InfoMessage("Syncrem daemon has started...");
			return Process::ForkedProcess;
		default:
			return Process::ForkingSuccesfull;
	};
}

/**
 * This is a wrapper for system function fork().
 * 
 * @return Same as fork().
 */

int Process::forkProcess() {
	int result = fork();
	if(result == 0) {
		switch(++level_) {
			case Process::Daemon :
				lock_.setMain(true);
				break;
			default :
				lock_.setMain(false);
				break;
		};
	}
	return result;
}

/**
 * This function checks whether process was terminated
 * by SIGTERM.
 * 
 * @return True if SIGTERM was sent.
 */

bool Process::isTerminated() {
	return (signalHandler_.hasSignal() &&
		signalHandler_.nextSignal() == SignalHandler::Terminate);
}
